﻿namespace ComTools.Helpers
{
    using System;
    using System.Collections.Generic;
    using System.Linq.Expressions;
    using System.Reflection;

    /// <summary>
    /// 该类提供了表达式的帮助方法
    /// </summary>
    public class ExpressionHelper
    {
        /// <summary>
        /// 根据表达式获取属性或字段的名称
        /// 列如： GetPropertyName&lt;Person&gt;( p => p.FirstName);
        /// </summary>
        /// <typeparam name="T">要获取的属性名称所属的类型</typeparam>
        /// <param name="exp">一个表达式，表示要获取的属性名称的类型的属性</param>
        /// <returns>属性或字段的名称</returns>
        public static string GetPropertyName<T>(Expression<Func<T, object>> exp)
        {
            MemberExpression memberExpression = null;

            // 获取成员表达式
            var expression = exp.Body as MemberExpression;

            // 判断是否是成员表达式
            if (expression != null)
                memberExpression = expression;

            // 将表达式转为一个一元表达式
            var unaryExpression = exp.Body as UnaryExpression;

            // 如果成功转为一元表达式则获取一元运算的操作数并判断是否是成员表达式
            if (unaryExpression?.Operand is MemberExpression)
                memberExpression = unaryExpression.Operand as MemberExpression;

            // 如果成员表达式为空，则提示该表达式不是字段或属性
            if (memberExpression == null)
                throw new InvalidOperationException("该表达式并非字段和属性");

            // 获取表达式中的成员属性
            var info = memberExpression.Member as PropertyInfo;

            return info.Name;
        }

        /// <summary>
        /// 根据表达式获取属性或字段的名称
        /// 列如： GetPropertyName(() => p.FirstName);
        /// </summary>
        /// <param name="exp">一个表达式，用于表示要获取的对象的字段</param>
        /// <returns>属性或字段的名称</returns>
        public static string GetPropertyName(Expression<Func<object>> exp)
        {
            var memberExpression = exp.Body as MemberExpression;
            if (memberExpression == null) throw new InvalidOperationException("Not a member access.");

            var info = memberExpression.Member as PropertyInfo;
            return info.Name;
        }

        /// <summary>
        /// 跟聚表达式获取属性或字段名称、属性或字段值
        /// 例如 
        /// var pName = string.Empty;
        /// var pValue = GetPropertyNameAndValue(() => p.FirstName,ref pName);
        /// </summary>
        /// <param name="exp">获取字段或属性的表达式</param>
        /// <param name="propName">ref参数，获取的属性名称</param>
        /// <returns>属性的值</returns>
        public static object GetPropertyNameAndValue(Expression<Func<object>> exp, ref string propName)
        {
            var memberExpression = exp.Body as MemberExpression;
            PropertyInfo propInfo = null;
            if (exp.Body is MemberExpression)
            {
                propInfo = ((MemberExpression)exp.Body).Member as PropertyInfo;
            }
            else if (exp.Body is UnaryExpression)
            {
                Expression op = ((UnaryExpression)exp.Body).Operand;
                propInfo = ((MemberExpression)op).Member as PropertyInfo;
            }

            object val = exp.Compile().DynamicInvoke();
            propName = propInfo.Name;
            return val;
        }
    }

    /// <summary>
    /// 运算类型表达式仓库
    /// </summary>
    class RepositoryExpressionTypeHelper
    {
        private static IDictionary<ExpressionType, string> _map = new Dictionary<ExpressionType, string>();

        /// <summary>
        /// 静态块：初始化对应的表达式和sql文本的关系
        /// </summary>
        static RepositoryExpressionTypeHelper()
        {
            _map[ExpressionType.Equal] = "=";
            _map[ExpressionType.NotEqual] = "<>";
            _map[ExpressionType.GreaterThanOrEqual] = ">=";
            _map[ExpressionType.LessThanOrEqual] = "<=";
            _map[ExpressionType.LessThan] = "<";
            _map[ExpressionType.GreaterThan] = ">";
        }

        /// <summary>
        /// 获取和表达式类型等效的sql文本
        /// <para>
        /// 仅支持 Equal、NotEqual、GreaterThanOrEqual、LessThanOrEqual、LessThan、GreaterThan
        /// </para>
        /// </summary>
        /// <param name="expType">表达式类型</param>
        /// <returns>sql的文本</returns>
        public static string GetText(ExpressionType expType)
        {
            if (!_map.ContainsKey(expType))
                throw new ArgumentException("expresion type :" + expType.ToString() + "not supported.");

            return _map[expType];
        }
    }
}
